package com.qianli.cashmama.product.server.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.log.factory.LogFactory;
import com.fqgj.log.interfaces.Log;
import com.google.common.collect.Lists;
import com.qianli.cashmama.product.facade.enums.*;
import com.qianli.cashmama.product.facade.result.ConfigResource;
import com.qianli.cashmama.product.facade.result.Product;
import com.qianli.cashmama.product.facade.result.ProductCategory;
import com.qianli.cashmama.product.server.domain.BusinessRuleDetailDomain;
import com.qianli.cashmama.product.server.domain.ProductCategoryDomain;
import com.qianli.cashmama.product.server.domain.ProductDomain;
import com.qianli.cashmama.product.server.domain.UserAuthTypeDomain;
import com.qianli.cashmama.product.server.entity.*;
import com.qianli.cashmama.product.server.exception.ProductException;
import com.qianli.cashmama.product.server.facade.ProductFacadeImpl;
import com.qianli.cashmama.product.server.mapper.ProductCategoryMapper;
import com.qianli.cashmama.product.server.mapper.ProductMapper;
import com.qianli.cashmama.product.server.mapper.ProductRuleDetailMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * Created by xuyang on 2017/7/19.
 */
@Service("productService")
public class ProductServiceImpl implements ProductService {
    private static final Log LOGGER = LogFactory.getLog(ProductServiceImpl.class);

    @Autowired
    private ProductCategoryMapper productCategoryMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductRuleDetailMapper productRuleDetailMapper;


    @Autowired
    private RedisClient redisClient;

    @Override
    public List<ProductCategoryDomain> getProductCategoryList(String appCode) {
        if (StringUtils.isBlank(appCode)) {
            throw new ProductException(ErrorCodeEnum.INVALID_APP_CODE);
        }
        return getProductCategoryByCode(appCode);
    }

    private List<ProductCategoryDomain> getProductCategoryByCode(String appCode) {
        List<ProductCategoryEntity> entities = productCategoryMapper.getCategoryByAppCode(appCode);
        List<ProductCategoryDomain> resultList = new ArrayList<>();
        for (ProductCategoryEntity entity : entities) {
            ProductCategoryDomain domain = entity.toDomain();
            resultList.add(domain);
        }
        return resultList;
    }

    @Override
    public List<UserAuthTypeDomain> getAllUserAuth() {
        List<UserAuthEntity> allUserAuth = productCategoryMapper.getAllUserAuth();
        if (CollectionUtils.isEmpty(allUserAuth)) {
            return Collections.emptyList();
        }
        List<UserAuthTypeDomain> userAuthTypeDomainList = Lists.newArrayList();
        for (UserAuthEntity entity : allUserAuth) {
            UserAuthTypeDomain domain = new UserAuthTypeDomain();
            BeanUtils.copyProperties(entity, domain);
            userAuthTypeDomainList.add(domain);
        }
        return userAuthTypeDomainList;
    }

    @Override
    public List<ProductDomain> getProductList(String productCategoryCode, boolean selectRules, boolean selectNotSell, ProductShelfTypeEnum shelfTypeEnum) {
        if (StringUtils.isBlank(productCategoryCode)) {
            throw new ProductException(ErrorCodeEnum.INVALID_PRODUCT_CATEGORY_CODE);
        }
        ProductCategoryEntity productCategoryEntity = productCategoryMapper.getByCode(productCategoryCode);
        if (productCategoryEntity == null) {
            throw new ProductException(ErrorCodeEnum.PRODUCT_CATEGORY_WAS_DELETED);
        }
        ProductCategoryDomain productCategoryDomain = productCategoryEntity.toDomain();
        if (productCategoryDomain.getIsSelling() == SellStatusEnum.NOT_ON_SELL) {
            throw new ProductException(ErrorCodeEnum.PRODUCT_CATEGORY_NOT_ON_SELL);
        }
        if (shelfTypeEnum == null) {
            shelfTypeEnum = ProductShelfTypeEnum.TYPE1;
        }

        List<ProductEntity> list = productMapper.getListByCategoryCode(productCategoryCode, shelfTypeEnum.getType());
        List<ProductDomain> resultList = new ArrayList<>();
        Set<String> codeSet = new HashSet<>();
        for (ProductEntity productEntity : list) {
            ProductDomain domain = productEntity.toDomain(productCategoryDomain);
            resultList.add(domain);
            codeSet.add(domain.getCode());
        }
        if (!selectNotSell && resultList.size() > 0) {
            final Date assertDate = new Date();
            resultList = resultList.stream()
                    .filter(x -> (x.getIsSelling() == SellStatusEnum.ON_SELL
                            && x.getStartTime().before(assertDate) && x.getEndTime().after(assertDate)))
                    .collect(Collectors.toList());
        }
        if (selectRules && resultList.size() > 0) {
            List<BusinessRuleDetailDomain> categoryRules = getRules(RuleOwnerTypeEnum.PRODUCT_CATEGORY,
                    productCategoryDomain.getCode());
            productCategoryDomain.setProductRules(categoryRules);

            Map<String, List<BusinessRuleDetailDomain>> map = getRulesByFunctional(RuleOwnerTypeEnum.PRODUCT, codeSet);
            for (ProductDomain productDomain : resultList) {
                productDomain.setProductRules(map.get(productDomain.getCode()));
            }
        }
        return resultList;
    }

    @Override
    public ProductDomain getProductDetail(String productCode, boolean selectRules, boolean selectNotSell) {
        if (StringUtils.isBlank(productCode)) {
            throw new ProductException(ErrorCodeEnum.INVALID_PRODUCT_CODE);
        }
        ProductEntity entity = productMapper.getByCode(productCode);
        if (entity == null) {
            return null;
        }
        ProductCategoryEntity productCategoryEntity = productCategoryMapper.getByCode(entity.getProductCategory());
        if (productCategoryEntity == null) {
            throw new ProductException(ErrorCodeEnum.PRODUCT_CATEGORY_WAS_DELETED);
        }
        ProductCategoryDomain productCategoryDomain = productCategoryEntity.toDomain();
        ProductDomain domain = entity.toDomain(productCategoryDomain);

        if (selectRules) {
            List<BusinessRuleDetailDomain> categoryRules = getRules(RuleOwnerTypeEnum.PRODUCT_CATEGORY,
                    productCategoryDomain.getCode());
            domain.getProductCategoryDomain().setProductRules(categoryRules);
            List<BusinessRuleDetailDomain> productRules = getRules(RuleOwnerTypeEnum.PRODUCT,
                    domain.getCode());
            domain.setProductRules(productRules);
        }
        return domain;
    }

    @Override
    public ProductDomain getProductDetail(String categoryCode, BigDecimal principalMoney, Integer period) {
        if (StringUtils.isBlank(categoryCode)) {
            throw new ProductException(ErrorCodeEnum.INVALID_PRODUCT_CATEGORY_CODE);
        }
        ProductCategoryEntity productCategoryEntity = productCategoryMapper.getByCode(categoryCode);
        if (productCategoryEntity == null) {
            throw new ProductException(ErrorCodeEnum.PRODUCT_CATEGORY_WAS_DELETED);
        }
        ProductShelfTypeEnum shelfTypeEnum = ProductShelfTypeEnum.TYPE1;
        ProductEntity entity = productMapper.getByMoneyAndPeriod(categoryCode, shelfTypeEnum.getType(), principalMoney, period);
        if (entity == null) {
            return null;
        }
        ProductCategoryDomain productCategoryDomain = productCategoryEntity.toDomain();
        return entity.toDomain(productCategoryDomain);
    }

    @Override
    public ProductDomain getProductDetailByMerchantCode(String merchantCode, BigDecimal principalMoney, Integer period) {
        if (StringUtils.isBlank(merchantCode)) {
            throw new ProductException(ErrorCodeEnum.INVALID_MERCHANT_CODE);
        }
        List<ProductCategoryEntity> productCategoryEntities = productCategoryMapper.getProductCategoriesByMerchantCode(merchantCode);
        if (CollectionUtils.isEmpty(productCategoryEntities)) {
            throw new ProductException(ErrorCodeEnum.PRODUCT_CATEGORY_WAS_DELETED);
        }
        ProductShelfTypeEnum shelfTypeEnum = ProductShelfTypeEnum.TYPE1;
        String categoryCode = productCategoryEntities.get(0).getCode();
        ProductEntity entity = productMapper.getByMoneyAndPeriod(categoryCode, shelfTypeEnum.getType(), principalMoney, period);
        if (entity == null) {
            return null;
        }
        ProductCategoryDomain productCategoryDomain = productCategoryEntities.get(0).toDomain();
        return entity.toDomain(productCategoryDomain);
    }

    private Map<String, List<BusinessRuleDetailDomain>> getRulesByFunctional(RuleOwnerTypeEnum ownerType, Set<String> ownerCodes) {
        List<ProductRuleDetailEntity> list = productRuleDetailMapper.getRulesByCodes(ownerType.getCode(), ownerCodes);
        List<BusinessRuleDetailDomain> domainList = new LinkedList<>();
        for (ProductRuleDetailEntity entity : list) {
            domainList.add(entity.convert());
        }
        return domainList.stream().collect(groupingBy(BusinessRuleDetailDomain::getOwnerCode, Collectors.toList()));
    }

    private List<BusinessRuleDetailDomain> getRules(RuleOwnerTypeEnum ownerType, String ownerCode) {
        List<ProductRuleDetailEntity> list = productRuleDetailMapper.getRules(ownerType.getCode(), ownerCode);
        List<BusinessRuleDetailDomain> domainList = new ArrayList<>();
        for (ProductRuleDetailEntity entity : list) {
            domainList.add(entity.convert());
        }
        return domainList;
    }

    @Override
    public List<ProductCategory> getAllActiveProductCategory() {
        List<ProductCategoryEntity> list = productCategoryMapper.getAllActiveProductCategory();
        List<ProductCategory> categoryList = Lists.newLinkedList();
        for (ProductCategoryEntity entity : list) {
            categoryList.add(entity.newFacade());
        }
        return categoryList;
    }

    @Override
    public ProductCategory getCategoryByCode(String productCategoryCode) {
        ProductCategoryEntity entity = productCategoryMapper.getCategoryByCode(productCategoryCode);
        if (entity == null) {
            return null;
        }
        ProductCategory productCategory = entity.newFacade();
        return productCategory;
    }

    private List<DictEntity> getDictList() {
        String json = redisClient.get(ProductCacheKeyEnum.DICT_LIST.getFormat());
        Optional<String> optional = Optional.ofNullable(json);
        if (optional.isPresent()) {
            return JSONArray.parseArray(optional.get(), DictEntity.class);
        }
        return Collections.emptyList();
    }

    @Override
    public String getDictName(String code) {
        List<DictEntity> allForCache = getDictList();
        return allForCache.stream().filter(dictEntity -> code.equals(dictEntity.getCode()))
                .findAny().orElse(new DictEntity()).getName();
    }

    private List<ConfigResourceEntity> getResourceList() {
        String json = redisClient.get(ProductCacheKeyEnum.CONFIG_RESOURCE_LIST.getFormat());
        Optional<String> optional = Optional.ofNullable(json);
        if (optional.isPresent()) {
            return JSONArray.parseArray(optional.get(), ConfigResourceEntity.class);
        }
        return Collections.emptyList();
    }

    @Override
    public List<ConfigResource> getResource(String code) {
        List<ConfigResourceEntity> allForCache = getResourceList();
        List<ConfigResource> configResourceList = Lists.newArrayList();
        allForCache.stream().filter(en -> code.equals(en.getCode()))
                .forEach(entity -> configResourceList.add(getConfigResource(entity)));
        return configResourceList;
    }

    private ConfigResource getConfigResource(ConfigResourceEntity entity) {
        ConfigResource configResource = new ConfigResource();
        entity.setConfigResource(StringUtils.isNotBlank(entity.getConfigResource()) ? entity.getConfigResource().replace("\n", "") : "");
        configResource.setCode(entity.getCode());
        configResource.setName(entity.getCode());
        configResource.setConfigResource(JSON.parseObject(entity.getConfigResource()));
        return configResource;
    }

    @Override
    public List<ProductCategory> getProductCategoriesByMerchantCode(String merchantCode) {
        List<ProductCategoryEntity> categoryAuthEntities = productCategoryMapper.getProductCategoriesByMerchantCode(merchantCode);
        if (CollectionUtils.isEmpty(categoryAuthEntities)) {
            return null;
        }

        List<ProductCategory> productCategories = new ArrayList<>();
        categoryAuthEntities.forEach(c -> productCategories.add(c.newFacade()));

        return productCategories;
    }

    @Override
    public List<Product> getProductByMerchantCode(String merchantCode) {
        List<ProductEntity> productEntities = productMapper.getProductByMerchantCode(merchantCode);
        List<ProductCategoryEntity> categoryAuthEntities = productCategoryMapper.getProductCategoriesByMerchantCode(merchantCode);
        if (CollectionUtils.isEmpty(categoryAuthEntities)) {
            return null;
        }

        if (CollectionUtils.isEmpty(productEntities)) {
            return null;
        }
        List<Product> products = new ArrayList<>();
        productEntities.forEach(productEntity -> products.add(productEntity.newFacade(categoryAuthEntities.get(0).newFacade())));
        return products;
    }

    @Override
    public ProductDomain getProductDetailByArrivalMoney(String categoryCode, BigDecimal arrivalMoney, Integer period, Integer type) {
        if (StringUtils.isBlank(categoryCode)) {
            throw new ProductException(ErrorCodeEnum.INVALID_PRODUCT_CATEGORY_CODE);
        }
        ProductCategoryEntity productCategoryEntity = productCategoryMapper.getByCode(categoryCode);
        if (productCategoryEntity == null) {
            throw new ProductException(ErrorCodeEnum.PRODUCT_CATEGORY_WAS_DELETED);
        }
        ProductEntity entity = productMapper.getByArrivalMoneyAndPeriod(categoryCode, type, arrivalMoney, period);
        if (entity == null) {
            return null;
        }
        ProductCategoryDomain productCategoryDomain = productCategoryEntity.toDomain();
        return entity.toDomain(productCategoryDomain);
    }

    @Override
    public boolean updateProductCategory(ProductCategory productCategory) {
        ProductCategoryEntity originEntity = productCategoryMapper.getByCode(productCategory.getCode());
        if (originEntity == null) {
            return false;
        }

        if (productCategory.getName() != null) {
            originEntity.setName(productCategory.getName());
        }

        if (productCategory.getSort() != null) {
            originEntity.setSort(productCategory.getSort());
        }
        if (productCategory.getApplyRate() != null) {
            originEntity.setApplyRate(productCategory.getApplyRate());
        }
        if (productCategory.getIsSelling() != null) {
            originEntity.setIsSelling(productCategory.getIsSelling());
        }
        if (productCategory.getCategoryType() != null) {
            originEntity.setCategoryType(productCategory.getCategoryType());
        }
        if (productCategory.getExtraInfo() != null) {
            originEntity.setExtraInfo(productCategory.getExtraInfo());
        }

        if (productCategory.getDailyApplyLimit() != null) {
            originEntity.setDailyApplyLimit(productCategory.getDailyApplyLimit());
        }
        if (productCategory.getForGp() != null) {
            originEntity.setForGp(productCategory.getForGp());
        }

        try {
            productCategoryMapper.updateByEntity(originEntity);
            //todo 没有日志- -
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public void addNewProductCategory(ProductCategory productCategory) {
        ProductCategoryEntity originEntity = new ProductCategoryEntity();
        BeanUtils.copyProperties(productCategory, originEntity);
        productCategoryMapper.insertNewOne(originEntity);
    }
}
